﻿use std::time::Duration;

use fake::faker::internet::en::SafeEmail;
use fake::faker::name::en::Name;
use fake::Fake;
use wiremock::matchers::{method, path};
use wiremock::{Mock, ResponseTemplate};

mod common;

use crate::common::*;

async fn create_unconfirmed_subscriber(app: &TestApp) -> ConfirmationLinks {
    // let body = "name=le%20guin&email=ursula_le_guin%40gmail.com";
    // 正在使用多个订阅者，其细节必须随机化，以避免冲突
    let name: String = Name().fake();
    let email: String = SafeEmail().fake();

    let body = serde_urlencoded::to_string(&serde_json::json!({
        "name":name,
        "email":email,
    }))
    .unwrap();

    // 通过 mount，只要底层的 MockServer 启动，我们指定的行为就会一直有效。
    // 而使用 mount_as_scoped，我们会得到一个 MockGuard 对象，超出范围时，会卸载相应行为。
    let _mock_guard = Mock::given(path("/email"))
        .and(method("POST"))
        .respond_with(ResponseTemplate::new(200))
        .named("Create unconfirmed subscriber")
        .expect(1)
        .mount_as_scoped(&app.email_server)
        .await;
    app.post_subscription(body)
        .await
        .error_for_status()
        .unwrap();
    // 检查 MockServer 收到的请求，获取确认链接。
    let email_request = app
        .email_server
        .received_requests()
        .await
        .unwrap()
        .pop()
        .unwrap();
    app.get_configuration_links(&email_request)
}

#[tokio::test]
async fn newsletter_are_not_delivered_to_unconfirmed_subscribers() {
    let app = spawn_app().await;
    create_unconfirmed_subscriber(&app).await;
    app.test_user.login(&app).await;

    Mock::given(path("/email"))
        .respond_with(ResponseTemplate::new(200))
        .expect(0) // 断言没有发送任务请求
        .mount(&app.email_server)
        .await;

    // 执行
    // 邮件简报负载结构的骨架。改为提交表单
    let newsletter_request_body = serde_json::json!({
        "title": "Newsletter title",
        "text_content": "Newsletter body as plain text",
        "html_content": "<p>Newsletter body as plain HTML</p>",
        "idempotency_key": uuid::Uuid::new_v4().to_string(),
    });
    let response = app.post_publish_newsletter(&newsletter_request_body).await;
    assert_is_redirect_to(&response, "/admin/newsletters");
    // 重定向
    let html_page = app.get_publish_newsletter_html().await;
    assert!(html_page.contains(
        "<p><i>The newsletter issue has been accepted - emails will go out shortly.</i></p>"
    ));

    app.dispatch_all_pending_emails().await;
    // Mock 在 Drop 上验证我们是否发送了邮件简报
}

async fn create_confirmed_subscriber(app: &TestApp) {
    let confirmation_link = create_unconfirmed_subscriber(app).await;
    reqwest::get(confirmation_link.html)
        .await
        .unwrap()
        .error_for_status()
        .unwrap();
}

#[tokio::test]
async fn newsletter_are_delivered_to_confirmed_subscribers() {
    let app = spawn_app().await;
    create_confirmed_subscriber(&app).await;
    app.test_user.login(&app).await;

    Mock::given(path("/email"))
        .and(method("POST"))
        .respond_with(ResponseTemplate::new(200))
        .expect(1)
        .mount(&app.email_server)
        .await;

    // 执行
    let newsletter_request_body = serde_json::json!({
        "title": "Newsletter title",
        "text_content": "Newsletter body as plain text",
        "html_content": "<p>Newsletter body as plain HTML</p>",
        "idempotency_key": uuid::Uuid::new_v4().to_string(),
    });
    let response = app.post_publish_newsletter(&newsletter_request_body).await;
    assert_is_redirect_to(&response, "/admin/newsletters");
    // 重定向
    let html_page = app.get_publish_newsletter_html().await;
    assert!(html_page.contains(
        "<p><i>The newsletter issue has been accepted - emails will go out shortly.</i></p>"
    ));
    app.dispatch_all_pending_emails().await;
    // Mock 在 Drop 上验证我们是否发送了邮件简报
}

#[tokio::test]
async fn you_must_be_logged_in_to_see_the_newsletter_form() {
    // Arrange
    let app = spawn_app().await;

    // Act
    let response = app.get_publish_newsletter().await;

    // Assert
    assert_is_redirect_to(&response, "/login");
}

#[tokio::test]
async fn you_must_be_logged_in_to_publish_a_newsletter() {
    // Arrange
    let app = spawn_app().await;

    // Act
    let newsletter_request_body = serde_json::json!({
        "title": "Newsletter title",
        "text_content": "Newsletter body as plain text",
        "html_content": "<p>Newsletter body as HTML</p>",
        "idempotency_key": uuid::Uuid::new_v4().to_string(),
    });
    let response = app.post_publish_newsletter(&newsletter_request_body).await;

    // Assert
    assert_is_redirect_to(&response, "/login");
}

#[tokio::test]
async fn newsletter_creation_is_idempotent() {
    // 准备
    let app = spawn_app().await;
    create_confirmed_subscriber(&app).await;
    app.test_user.login(&app).await;

    Mock::given(path("/email"))
        .and(method("POST"))
        .respond_with(ResponseTemplate::new(200))
        .expect(1) // 重要
        .mount(&app.email_server)
        .await;

    // 执行——第一部分，提交表单
    let newsletter_request_body = serde_json::json!({
        "title": "newsletter title",
        "text_content": "Newsletter body as plain text",
        "html_content": "<p>Newsletter body as plain HTML</p>",
        // 幂等键做为表单数据的一部分
        "idempotency_key": uuid::Uuid::new_v4().to_string(),
    });
    let response = app.post_publish_newsletter(&newsletter_request_body).await;
    assert_is_redirect_to(&response, "/admin/newsletters");

    // 执行——第二部分，跟随重定向
    let html_page = app.get_publish_newsletter_html().await;
    assert!(html_page.contains(
        "<p><i>The newsletter issue has been accepted - emails will go out shortly.</i></p>"
    ));

    // 执行——第三部分，再次提交表单
    let response = app.post_publish_newsletter(&newsletter_request_body).await;
    assert_is_redirect_to(&response, "/admin/newsletters");

    // 执行——第四部分，跟随重定向
    let html_page = app.get_publish_newsletter_html().await;
    assert!(html_page.contains(
        "<p><i>The newsletter issue has been accepted - emails will go out shortly.</i></p>"
    ));

    app.dispatch_all_pending_emails().await;
    // Mock 在 Drop 上验证我们是否再次发送了邮件简报
}

#[tokio::test]
async fn concurrent_form_submission_is_handled_gracefully() {
    // 准备
    let app = spawn_app().await;
    create_confirmed_subscriber(&app).await;
    app.test_user.login(&app).await;

    Mock::given(path("/email"))
        .and(method("POST"))
        .respond_with(ResponseTemplate::new(200).set_delay(Duration::from_secs(2)))
        .expect(1)
        .mount(&app.email_server)
        .await;

    // 执行——并发提交两个邮件简报表单
    let newsletter_request_body = serde_json::json!({
        "title": "Newsletter title",
        "text_content": "Newslettter body as plain text",
        "html_content": "<p>Newslettter body as plain text</p>",
        "idempotency_key": uuid::Uuid::new_v4().to_string(),
    });
    let response1 = app.post_publish_newsletter(&newsletter_request_body);
    let response2 = app.post_publish_newsletter(&newsletter_request_body);
    let (response1, response2) = tokio::join!(response1, response2);

    assert_eq!(response1.status(), response2.status());
    assert_eq!(
        response1.text().await.unwrap(),
        response2.text().await.unwrap()
    );

    app.dispatch_all_pending_emails().await;
    // Mock 在 Drop 上验证我们是否发送了邮件简报
}

// 重新设计后，它已不再需要。
// fn when_sending_an_email() -> MockBuilder {
//     Mock::given(path("/email")).and(method("POST"))
// }

// #[tokio::test]
// async fn transient_errors_do_not_cause_duplicate_deliveries_on_retries() {
//     // 准备
//     let app = spawn_app().await;
//     let newsletter_request_body = serde_json::json!({
//         "title": "Newsletter title",
//         "text_content": "Newslettter body as plain text",
//         "html_content": "<p>Newslettter body as HTML</p>",
//         "idempotency_key": uuid::Uuid::new_v4().to_string(),
//     });
//     // 两个订阅者，而不是一个
//     create_confirmed_subscriber(&app).await;
//     create_confirmed_subscriber(&app).await;
//     app.test_user.login(&app).await;

//     // 第一部分——提交邮件简报表单
//     // 第二个订阅者的电子邮件发送失败
//     when_sending_an_email()
//         .respond_with(ResponseTemplate::new(200))
//         .up_to_n_times(1)
//         .expect(1)
//         .mount(&app.email_server)
//         .await;
//     when_sending_an_email()
//         .respond_with(ResponseTemplate::new(500))
//         .up_to_n_times(1)
//         .expect(1)
//         .mount(&app.email_server)
//         .await;

//     let response = app.post_publish_newsletter(&newsletter_request_body).await;
//     assert_eq!(response.status(), StatusCode::INTERNAL_SERVER_ERROR);

//     // 第二部分——重试提交表单
//     // 现在两个订阅者的电子邮件都发送成功
//     when_sending_an_email()
//         .respond_with(ResponseTemplate::new(200))
//         .expect(1)
//         .named("Delivery retry")
//         .mount(&app.email_server)
//         .await;
//     let response = app.post_publish_newsletter(&newsletter_request_body).await;
//     assert_eq!(response.status(), StatusCode::SEE_OTHER);

//     app.dispatch_all_pending_emails().await;
//     // Mock 在 Drop 上验证没有重复发送
// }
